package com.sdy.hospitalguard.embed.keepalive.manager.netty.hander;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sdy.hospitalguard.embed.keepalive.manager.netty.factory.ChannelFactory;
import com.sdy.hospitalguard.embed.keepalive.manager.netty.factory.HttpResponseFactory;
import com.sdy.hospitalguard.embed.keepalive.pojo.device.heart.HeartBack;
import com.sdy.hospitalguard.embed.keepalive.pojo.device.heart.HeartReportData;
import com.sdy.hospitalguard.embed.keepalive.pojo.device.heart.HeartRequestDTO;
import com.sdy.hospitalguard.embed.keepalive.pojo.netty.FastGateChannelHandlerContext;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

@Slf4j
public class HttpHeartHandler extends SimpleChannelInboundHandler {

    private static final String HEART_REPORT = "/LAPI/V1.0/PACS/Controller/HeartReportInfo";
    private final String X_FRAME_OPTIONS = "X-Frame-Options";
    private final String SAMEORIGIN = "SAMEORIGIN";
    private final String TEXT_PLAIN = "text/plain";
    public static final String CONTENT_LENGTH = "Content-Length";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String CONNECTION = "Connection";
    public volatile String serialNo;

    /**
     * 读取通道,过滤并处理心跳消息
     *
     * @param ctx 通道上下文
     * @param msg 消息
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            // 区分是response 还是 request
            if (msg instanceof FullHttpRequest) {
                FullHttpRequest httpRequest = (FullHttpRequest) msg;
                String heartbeat = httpRequest.content().toString(CharsetUtil.UTF_8);
                if (httpRequest.uri().contains(HEART_REPORT)){
                    // 回包并存下ctx
                    saveDeviceCtx(heartbeat, ctx);
                    getHeartInfo(ctx);
                }else{
                    ctx.fireChannelRead(msg);
                }
            } else {
                ctx.fireChannelRead(msg);
            }
        } catch (Exception e) {
            log.error("Read [{}] channel msg[{}] failed for:{}", ctx.channel().remoteAddress().toString()
                    , JSON.toJSONString(msg), e.getMessage(), e);
        }
    }

    /**
     * 心跳回包
     */
    private void getHeartInfo(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(getHeartBackBytebuff());
        log.info("[SERIAL_NO][{}], 心跳回包正常！客户端IP: {}", serialNo, ctx.channel().remoteAddress());
    }

    /**
     * 心跳回包拼装
     *
     * @return
     * @throws Exception
     */
    private FullHttpResponse getHeartBackBytebuff() throws Exception {
        HeartBack heartBack = new HeartBack();
        HeartReportData data = new HeartReportData();
        heartBack.setData(data);
        String strBody = JSONObject.toJSONString(heartBack);
        FullHttpResponse response = new DefaultFullHttpResponse(
                HTTP_1_1, OK, Unpooled.wrappedBuffer(strBody.getBytes()));
        response.headers().set(CONTENT_LENGTH,
                response.content().readableBytes());
        response.headers().set(CONTENT_TYPE, TEXT_PLAIN);
        response.headers().set(CONNECTION, HttpHeaderValues.CLOSE);
        response.headers().set(X_FRAME_OPTIONS, SAMEORIGIN);
        return response;
    }

    /**
     * 保存链接
     * @param strRequest
     * @param ctx
     * @throws Exception
     */
    private void saveDeviceCtx(String strRequest, ChannelHandlerContext ctx) throws Exception {
        HeartRequestDTO request = JSONObject.parseObject(strRequest, HeartRequestDTO.class);
        String serialNo = request.getStrDeviceCode();
        this.serialNo = serialNo;
        FastGateChannelHandlerContext fctx = ChannelFactory.getChannelBySerialNo(serialNo);
        if(Objects.isNull(fctx)){
            ChannelFactory.addChannel(serialNo, ctx);
        }else if(Objects.isNull(fctx.getCtx())){
            ChannelFactory.reFrushChannel(serialNo, ctx);
        }

//        ChannelFactory.addChannel(serialNo, ctx);
    }

    /**
     * 添加channel时
     * */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        log.debug("SimpleChatClient:" + incoming.remoteAddress()+"通道被添加");
    }

    /**
     * 删除channel时
     * */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        log.debug("SimpleChatClient:" + incoming.remoteAddress()+"通道被删除");
    }

    /**
     * 服务端监听到客户端不活动
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 服务端接收到客户端掉线通知
        Channel incoming = ctx.channel();
        log.error("SimpleChatClient: {}  掉线", incoming.remoteAddress() );
    }

    /**
     * 服务端监听到客户端活动
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 服务端接收到客户端上线通知
        Channel incoming = ctx.channel();
        log.debug("SimpleChatClient:" + incoming.remoteAddress() + "在线");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel incoming = ctx.channel();
        log.error("SimpleChatClient:{}", incoming.remoteAddress() + "异常 : " + cause);
        // 出现异常判断下通道是否还可用
        if (!incoming.isWritable()) {
            ctx.close();
            HttpResponseFactory.removeResponse(ctx);
        }
    }
}
